#ifndef GLSTUDY_VERTEXLAYOUT_H
#define GLSTUDY_VERTEXLAYOUT_H
#include "Render.h"
#include <vector>
#include <type_traits>
// 判断是否为 OPENGL 定义的类型

template <typename T>
struct is_gltype : public std::false_type
{
};
template <>
struct is_gltype<float> : public std::true_type
{
};

template <>
struct is_gltype<unsigned int> : public std::true_type
{
};
template <>
struct is_gltype<unsigned char> : public std::true_type
{
};
#if 0  //未启用代码 glType
// OpenGL 定义类型枚举与对应的宏的 映射
template <unsigned int type_enum>
struct gltype
{
	using type = void;
};

template <>
struct gltype<GL_FLOAT>
{
	using type = float;
};
template <>
struct gltype<GL_UNSIGNED_INT>
{
	using type = unsigned int;
};
template <>
struct gltype<GL_UNSIGNED_BYTE>
{
	using type = unsigned char;
};
#endif // 未启用代码 glType
/**
 * @brief VertexLayout  链接顶点属性 的抽象
 * glVertexAttribPointer()
 *
 * 可以理解为顶点的内存布局，每一个 LayoutItem 都是一个 链接属性，
 * VAO想设置顶点链接属性，只需要绑定 该内存布局对象即可
 *
 */
class VertexLayout
{
public:
	/**
	 * @brief LayoutItem  glVertexAttribPointer对应的内存布局
	 */
	struct LayoutItem
	{
		unsigned int m_type;  //类型枚举
		unsigned int m_count; //该类型的数量

		unsigned int m_offset; //偏移量
		int m_normalized;	   //是否标准化
		LayoutItem() : m_type(0), m_count(0), m_offset(0), m_normalized(0){};
		LayoutItem(unsigned int type, unsigned int count) : m_type(type), m_count(count), m_normalized(GL_FALSE)
		{
			m_offset = 0;
			switch (type)
			{
			case GL_FLOAT:
				m_offset += m_count * sizeof(float);
				break;
			case GL_UNSIGNED_BYTE:
				m_offset += m_count * sizeof(unsigned char);
				break;
			case GL_UNSIGNED_INT:
				m_offset += m_count * sizeof(unsigned int);
				break;
			default:
				assert(0); // type is not OPenGL TypeEnum
				break;
			}
		}
	};

public:
	VertexLayout() : m_stride(0) {}

	/**
	 * @brief 以初始化列表的方式 构造 Layout
	 *
	 * @param vec
	 *
	 * @note 例如 ： 初始化Vec2,Vec3的内存布局 ： { { {GL_FLOAT,2},{GL_FLOAT,3} } }
	 */
	VertexLayout(const std::vector<LayoutItem> &vec)
	{
		m_stride = 0;
		for (const auto &it : vec)
		{
			m_LayoutItems.push_back(it);
			m_stride += it.m_offset;
		}
	}
	/**
	 * @brief VAO 设置对应的Layout
	 *
	 * @param va
	 */
	void bindToVertexArray(VertexArray &va) const
	{
		va.bind();
		unsigned int offset = 0;
		for (unsigned int i = 0; i < m_LayoutItems.size(); ++i)
		{
			auto &item = m_LayoutItems[i];
			glVertexAttribPointer(i, item.m_count, item.m_type, item.m_normalized, m_stride, reinterpret_cast<const void *>(offset));
			glEnableVertexAttribArray(i);
			offset += item.m_offset;
		}
	}

	/**
	 * @brief 添加布局LayoutItem
	 *
	 * @tparam T  GL_TYPE 类型
	 * @param count 该类型的数量
	 */
	template <typename T>
	void push(unsigned int count);

	inline const std::vector<LayoutItem> &layoutItems() const { return m_LayoutItems; }
	inline unsigned int stride() const { return m_stride; }

private:
	std::vector<LayoutItem> m_LayoutItems;
	unsigned int m_stride;
};
template <typename T>
void VertexLayout::push(unsigned int count)
{
	static_assert(is_gltype<T>::value, "push Type is unknow!");
}
template <>
void VertexLayout::push<float>(unsigned int count)
{
	m_LayoutItems.push_back({GL_FLOAT, count});
	m_stride += sizeof(GLfloat) * count;
}
template <>
void VertexLayout::push<unsigned int>(unsigned int count)
{
	m_LayoutItems.push_back({GL_UNSIGNED_INT, count});
	m_stride += sizeof(unsigned int) * count;
}
template <>
void VertexLayout::push<unsigned char>(unsigned int count)
{
	m_LayoutItems.push_back({GL_UNSIGNED_BYTE, count});
	m_stride += sizeof(unsigned char) * count;
}

#endif // GLSTUDY_VERTEXLAYOUT_H
